package com.lfy.modules.safety.aop;


import com.lfy.modules.safety.aop.anno.CurrentLimit;
import com.lfy.modules.common.domain.BaseEnum;
import com.lfy.modules.common.domain.R;
import com.lfy.modules.safety.limit.BaseLimit;
import com.lfy.modules.safety.limit.LimitFactory;
import com.lfy.modules.common.utils.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
/***
 * 限流切面
 *
 * @author <a href="https://www.lvfeiy.top">吕飞扬</a>
 * @date 2024/4/25 0:54
 */
@Aspect
@Component
@Order(1)
@Slf4j
public class LimitAspect {
    @Pointcut("@annotation(com.lfy.modules.safety.aop.anno.CurrentLimit)||@annotation(com.lfy.modules.safety.aop.anno.CurrentLimitContaion)")
    public void cut(){

    }
    @Around("cut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        CurrentLimit[] currentLimits = methodSignature.getMethod().getAnnotationsByType(CurrentLimit.class);
        int limitCount = 0;
        boolean flag = false;
        for (CurrentLimit currentLimit : currentLimits) {
            limitCount++;
            //次数
            int once = currentLimit.once();
            //时间
            int time = currentLimit.time();
            TimeUnit nuit = currentLimit.nuit();
            //key
            String key = currentLimit.strategy().toDo();
            if(key.startsWith(RequestUtil.LOCAL_REMOTE_HOST1) || key.startsWith(RequestUtil.LOCAL_REMOTE_HOST2)
                    || key.startsWith(RequestUtil.LOCAL_REMOTE_HOST3)|| key.startsWith(RequestUtil.LOCAL_REMOTE_HOST4)){
                //本地不限制
                break;
            }
            key = key.concat("@index_").concat(limitCount + "");
            BaseLimit baseLimit = LimitFactory.getInstance();
            boolean limited = baseLimit.isLimited(key, toM(time, nuit), once,currentLimit.isBlack());

            if (limited) {
               flag = true;
               log.info("限流：{}",key);
               break;
            }
            baseLimit.setCount(key);
        }
        if (flag) {
            return R.error(BaseEnum.commonEnum.FREQUENCY_LIMIT);
        }

        return joinPoint.proceed();
    }
    private static long toM(int time , TimeUnit nuit){
        return nuit.toSeconds(time) * 1000;
    }
}
